/*
Name: sendmmsgTest.cpp
Author: Sean Sill
Description: A simple test to make sure sendmmsg works correctly
*/

#include <signal.h>
#include <string>
#include <ola/base/Flags.h>
#include <ola/Clock.h>
#include <ola/io/IOQueue.h>
#include <ola/Logging.h>
#include <ola/network/IPV4Address.h>
#include <ola/network/NetworkUtils.h>
#include <ola/network/SocketAddress.h>
#include <ola/network/Socket.h>

#include <vector>
#include <iostream>
#include <sys/time.h>

using ola::io::IOQueue;
using ola::network::HostToNetwork;
using ola::network::IPV4Address;
using ola::TimeStamp;
using ola::Clock;
using ola::TimeInterval;
using ola::network::IPV4SocketAddress;
using ola::network::NetworkToHost;
using ola::network::UDPSocket;
using std::cout;
using std::string;
using std::vector;

static const unsigned char test_cstring[] = "DFDFDFDFDFDFDFDFDFDFDFD";

DEFINE_uint16(iterations, 100, "number of iterations");
DEFINE_uint16(ports, 1, "number of ports");
DEFINE_uint16(base_port, 10000, "base port");
DEFINE_uint16(ips, 1, "number of ips");
DEFINE_string(base_ip, "192.168.1.1", "base ip");


void build_targets(vector<IPV4SocketAddress> *targets) {
  std::vector<IPV4SocketAddress> ip_list;
  IPV4Address base_ip = IPV4Address::FromStringOrDie(FLAGS_base_ip.str());
  uint32_t ip_as_int = NetworkToHost(base_ip.AsInt());

  for (uint32_t ip = ip_as_int; ip < ip_as_int + FLAGS_ips; ip++) {
    for (uint16_t port = FLAGS_base_port; port < FLAGS_base_port + FLAGS_ports;
         port++) {
      IPV4Address ip_addr(HostToNetwork(ip));
      IPV4SocketAddress socket(ip_addr, port);
      OLA_INFO << "Added target: " << socket;
      targets->push_back(socket);
    }
  }
}

long test(const vector<IPV4SocketAddress> &targets) {
  UDPSocket sock;

  TimeStamp begin, end;
  Clock clock;
  long test_time;
  IPV4SocketAddress sock_addr(IPV4Address::WildCard(), 10000);

  cout << "Begining Sendmmsg Test \n";

  // Attempt to initialize socket
  if(!sock.Init()) {
    OLA_FATAL << "Could not initialize the socket\n";
    return 0;
  }

  // Bind to 0.0.0.0
  if(!sock.Bind(sock_addr)) {
    OLA_FATAL << "Could not bind to: "  << sock_addr << "\n";
  }


  IOQueue output;
  output.Write(static_cast<const uint8_t*>(test_cstring), sizeof(test_cstring));

  /* We only care if sendmmsg is actually decreasing the amount of time we
       spend in sending multiple of the same packet to many ip addresses*/
  clock.CurrentTime(&begin);
  ssize_t packets_sent = sock.SendMultipleTo(&output, targets);
  clock.CurrentTime(&end);

  TimeInterval duration = end - begin;

  test_time = duration.Seconds() * 1000000 + duration.MicroSeconds();

  if(packets_sent < 0){
    OLA_FATAL << "Could not send on  " << sock_addr << "\n";
    return 0;
  }

  cout << packets_sent << " packets sent in " << test_time <<
      " microseconds!\n";
  cout << "Closing the socket! Sent " << packets_sent << " packets \n";
  sock.Close();

  return test_time;
}

int main(int argc, char* argv[]) {
  ola::SetHelpString("[options]", "Start the OLA Daemon.");
  ola::ParseFlags(&argc, argv);
  ola::InitLoggingFromFlags();

  vector<IPV4SocketAddress> targets;
  build_targets(&targets);

  vector<long> results;
  long avg = 0;

  for (uint16_t i = 0; i < FLAGS_iterations; ++i) {
    cout << "*****************************Test " << i <<
        " **************************\n";
    long test_time = test(targets);
    avg += test_time;
    results.push_back(test_time);
  }

  avg = avg / results.size();
  cout << "\n\n Completed " << results.size()
       << " tests with an average time of " << avg << " microseconds.\n";

  return 0;
}
